package cn.tedu.hongbeifang.config;

import cn.tedu.hongbeifang.filters.JwtAuthorizationFilter;
import cn.tedu.hongbeifang.web.JsonResult;
import cn.tedu.hongbeifang.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Spring Security的配置类
 * 配置默认的登录表单( http.formLogin();)
 * 配置请求的访问控制(http.authorizeRequests())等等
 *
 * @Auther:fzd
 * @Date:2023/3/6
 */
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //处理密码加密
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 配置Spring Security创建Session的策略：STATELESS=从不使用Session，NEVER=不主动创建Session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        // 将JWT过滤器添加到Spring Security框架的过滤器链中合适的位置
        http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        //处理未通过认证时导致的拒绝访问
        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                log.debug("开始处理未登录的业务");
                response.setContentType("application/json;charset=utf-8");
                PrintWriter writer = response.getWriter();
               /* writer.println("{\n"+
                        "  \"state\":40100,\n"+
                        "  \"message\":\"您当前未登录,请登录!\"\n"+"}");*/
                JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERROR_UNAUTHORIZED, "您当前未登录，请登录！");
                String jsonString = JSON.toJSONString(jsonResult);
                writer.println(jsonString);
                writer.close();
            }
        });

        //禁用"防止伪造的跨域机制" 的防御机制
        http.csrf().disable();

        //解决复杂跨域访问问题
        http.cors();

        log.debug("开始处理查询白名单业务");
        //认证白名单
        String[] urls = {
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources",
                "/v2/api-docs",
                "/login"
        };
        // 基于请求的访问控制
        http.authorizeRequests() // 对请求进行授权
                /*这种方法可以更直观的解决复杂跨域访问的问题
                .mvcMatchers(HttpMethod.OPTIONS)
                .permitAll()*/
                .mvcMatchers(urls) // 匹配某些路径
                .permitAll() // 直接许可，即不需要认证即可访问
                .anyRequest() // 任意请求
                .authenticated(); // 要求通过认证的


        // 如果调用以下方法，当需要访问通过认证的资源，但是未通过认证时，将自动跳转到登录页面
        // 如果未调用以下方法，将响应403
        //http.formLogin();

        // super.configure(http); // 不要保留调用父类同名方法的代码，不要保留！不要保留！不要保留！
    }

}
